import { BaseGraph, IBaseProps, IBoundBox } from './graph/base'
import { Group } from './graph/group'

export interface ISceneProps extends IBaseProps {
  zoom?: boolean
  pan?: boolean
  max?: number
  min?: number
}

export default class Scene extends Group {
  public children: Array<BaseGraph> = []
  public canvas: HTMLCanvasElement = null
  public zoom: boolean = true
  public pan: boolean = true
  public min: number = 0.3
  public max: number = 10
  public ctx: CanvasRenderingContext2D = null
  private mousedownPosition = { x: -1, y: -1 }
  private isMousedown = false
  private oldMatrix = []
  private devicePixelRatio = 1
  public height: number = 0
  public width: number = 0
  public centerX: number = 0
  public centerY: number = 0
  constructor(canvas: HTMLCanvasElement, props?: ISceneProps) {
    super(props)
    this.canvas = canvas
    this.ctx = canvas.getContext('2d')
    this.zoom = props?.zoom == undefined ? this.zoom : props?.zoom
    this.pan = props?.pan == undefined ? this.pan : props?.pan
    this.min = props?.min || this.min
    this.max = props?.max || this.max
    this.devicePixelRatio = window.devicePixelRatio || 1
    // 高清适配
    this.matrix.matrix = this.matrix.scaleTo(this.devicePixelRatio, this.devicePixelRatio)
    this.addEventListener()
    window.addEventListener('resize', () => this.resize())
    this.resize()
    // this.animate()
  }
  public render() {
    this.draw(this.ctx)
  }
  public animate() {
    this.render()
    requestAnimationFrame(() => this.animate())
  }
  /**
   * 重置尺寸
   */
  public resize() {
    this.width = this.canvas.clientWidth
    this.height = this.canvas.clientHeight
    this.centerX = this.width / 2
    this.centerY = this.height / 2
    this.canvas.style.width = this.canvas.clientWidth + 'px'
    this.canvas.style.height = this.canvas.clientHeight + 'px'
    this.canvas.width = this.canvas.clientWidth * this.devicePixelRatio
    this.canvas.height = this.canvas.clientHeight * this.devicePixelRatio
  }
  public draw(ctx: CanvasRenderingContext2D): void {
    // 清除因为变换清除不掉的像素
    ctx.resetTransform()
    // 清除画布
    ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
    // 变换矩阵
    // @ts-ignore
    ctx.setTransform(...this.matrix.matrix)
    // 绘制模型
    for (let i = 0; i < this.children.length; i++) {
      this.children[i].draw(ctx)
    }
  }
  public clearRect(x: number, y: number, width: number, height: number) {
    this.ctx.clearRect(x, y, width, height)
  }
  private addEventListener() {
    this.canvas.addEventListener('mousedown', this.onMousedown.bind(this), false)
    this.canvas.addEventListener('mousemove', this.onMousemove.bind(this), false)
    this.canvas.addEventListener('mouseup', this.onMouseup.bind(this), false)
    this.canvas.addEventListener('mouseleave', this.onMouseup.bind(this), false)
    this.canvas.addEventListener('wheel', this.onWheel.bind(this), false)
  }
  private onMousedown(event: MouseEvent) {
    let { x, y } = this.getOffsetPosition(event)
    this.mousedownPosition = { x, y }
    this.oldMatrix = [...this.matrix.matrix]
    if (this.pan) this.isMousedown = true
  }
  private onMousemove(event: MouseEvent) {
    if (this.isMousedown) {
      let { x, y } = this.getOffsetPosition(event)
      let ox = x - this.mousedownPosition.x
      let oy = y - this.mousedownPosition.y
      this.matrix.matrix = this.matrix.multiply(this.matrix.translateMatrix(ox, oy), this.oldMatrix)
    }
  }
  private onMouseup(event: MouseEvent) {
    let { x, y } = this.getOffsetPosition(event)
    this.isMousedown = false
    if (this.mousedownPosition.x == x && this.mousedownPosition.y == y) {
    }
  }
  private getStartPosition() {
    let x = parseInt(this.canvas.style.borderLeftWidth) || 0
    let y = parseInt(this.canvas.style.borderTopWidth) || 0
    return { x, y }
  }
  private getOffsetPosition(event: MouseEvent) {
    let start = this.getStartPosition()
    return { x: event.offsetX * this.devicePixelRatio - start.x, y: event.offsetY * this.devicePixelRatio - start.y }
  }
  private onWheel(event: WheelEvent) {
    this.zoomEvent(event)
  }
  private zoomEvent(event: WheelEvent) {
    if (!this.zoom) return
    let scale = 1
    let matrix = this.matrix
    if (event.deltaY > 0) {
      if (matrix.matrix[0] >= matrix.max) return
      scale = 1.05
    } else if (event.deltaY < 0) {
      if (matrix.matrix[0] <= matrix.min) return
      scale = 0.95
    }
    let { x, y } = this.getOffsetPosition(event)
    this.matrix.scaleByPoint(scale, scale, x, y)
  }
  public getBoundBox(): IBoundBox {
    return {
      top: 0,
      left: 0,
      bottom: this.canvas.width,
      right: this.canvas.height,
      width: this.canvas.width,
      height: this.canvas.height
    }
  }
  public getUrl() {
    return this.canvas.toDataURL('image/png')
  }
  public setPath() {}
  public drawPath(ctx: CanvasRenderingContext2D): void {}
}
